package com.trhui.mallbook.client;

import com.trhui.mallbook.common.exception.FailureCode;
import com.trhui.mallbook.common.exception.SdkClientException;
import com.trhui.mallbook.common.http.HttpUtil;
import com.trhui.mallbook.common.rsa.RsaUtil;
import com.trhui.mallbook.common.utils.JsonUtil;
import com.trhui.mallbook.common.utils.StringUtil;
import com.trhui.mallbook.common.utils.ValidationUtil;
import com.trhui.mallbook.config.MallBookConfig;
import com.trhui.mallbook.constant.MallBookConstant;
import com.trhui.mallbook.domain.common.ApiRequest;
import com.trhui.mallbook.domain.common.ApiResponse;
import com.trhui.mallbook.domain.common.BaseResponse;
import com.trhui.mallbook.enums.ServerCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 抽象客户端
 *
 * @author linqy
 */
public abstract class AbstractClient {

    private static Logger logger = LoggerFactory.getLogger(AbstractClient.class);

    /**
     * 发送请求
     *
     * @param request           请求参数
     * @param returnTagClassOfT 返回类型
     * @return 返回结果
     */
    static <T> BaseResponse<T> post(ApiRequest request, Class<T> returnTagClassOfT) {
        // 使用request.getParams()和request.getExtraParams()进行合并
        Map<String, Object> params = JsonUtil.toMap(request.getParams());
        if (request.getExtraParams() != null) {
            params.putAll(request.getExtraParams());
        }
        //
        MallBookConfig config = MallBookConfig.getConfig(request.getMerchantNo());

        ValidationUtil.checkNotNullAndThrow(request.getMerOrderId(), "MallBook请求订单号必填");

        request.setParams(JsonUtil.toJsonStr(params));

        request.setMerchantNo(config.getMerchantNo());
        request.setVersion(StringUtil.isBlank(request.getVersion()) ? config.getVersion() : request.getVersion());
        long timestamp = System.currentTimeMillis();
        request.setDate(String.valueOf(timestamp));
        request.setSign(RsaUtil.sign(request.toString(), config.getMerchantPrivateKey()));

        Map<String, Object> paramMap = buildRequestParam(request);
        String url = getUrl(config);

        boolean isDebug = config.isDebug();
        boolean isFileUploadCode = ServerCodeEnum.FILE_UPLOAD.getCode().equals(request.getServerCode());
        boolean isFileUploadDebug = config.isFileUploadDebug();
        boolean shouldLog = isDebug && (!isFileUploadCode || isFileUploadDebug);

        if (isDebug) {
            logger.info("MallBook请求地址：{}", url);
        }
        if (shouldLog) {
            logger.info("MallBook请求参数:{}", JsonUtil.toJsonStr(paramMap));
        }

        String resultJson = HttpUtil.post(url, config.getTimeout(), paramMap);

        if (isDebug) {
            logger.info("MallBook响应参数:{}", resultJson);
        }
        if (StringUtil.isBlank(resultJson)) {
            throw new SdkClientException(FailureCode.HTTP_TIMEOUT_EXCEPTION);
        }

        ApiResponse apiResponse = JsonUtil.toBean(resultJson, ApiResponse.class);

        String publicKey = getMallBookPublicKey(config);

        doCheck(publicKey, apiResponse);

        BaseResponse<T> returnT = new BaseResponse<>(apiResponse);
        if (MallBookConstant.SUCCESS_CODE.equals(apiResponse.getCode())) {
            returnT.setData(JsonUtil.toBean(apiResponse.getResult(), returnTagClassOfT));
        }
        return returnT;
    }

    /**
     * 获取MallBook请求地址
     *
     * @param config 配置
     * @return 请求地址
     */
    private static String getUrl(MallBookConfig config) {

        if (StringUtil.isNotBlank(config.getApiBase())) {
            return config.getApiBase();
        }
        return config.isProdEnvironment() ? MallBookConstant.URL_PROD : MallBookConstant.URL_UAT;

    }

    /**
     * 获取MallBook公钥
     *
     * @param config 配置
     * @return 公钥
     */
    private static String getMallBookPublicKey(MallBookConfig config) {

        if (StringUtil.isNotBlank(config.getMallBookPublicKey())) {
            return config.getMallBookPublicKey();
        }
        return config.isProdEnvironment() ? MallBookConstant.PUBLIC_KEY_PROD : MallBookConstant.PUBLIC_KEY_UAT;

    }

    /**
     * 构建请求参数
     *
     * @param request 请求参数
     * @return 请求参数
     */
    private static Map<String, Object> buildRequestParam(ApiRequest request) {
        Map<String, Object> paramMap = new HashMap<>(12);
        paramMap.put("merOrderId", request.getMerOrderId());
        paramMap.put("merchantNo", request.getMerchantNo());
        paramMap.put("channelType", request.getChannelType());
        paramMap.put("sign", request.getSign());
        paramMap.put("serverCode", request.getServerCode());
        paramMap.put("version", request.getVersion());
        paramMap.put("params", request.getParams());
        paramMap.put("date", request.getDate());
        return paramMap;
    }

    /**
     * 验签
     *
     * @param publicKey   公钥
     * @param apiResponse 响应
     */
    private static void doCheck(String publicKey, ApiResponse apiResponse) {
        boolean check = RsaUtil.doCheck(apiResponse.toString(), apiResponse.getSign(), publicKey, "UTF-8");
        if (!check) {
            throw new SdkClientException("接口响应成功，验签失败");
        }
    }

}
